#include <stdlib.h>
#include <stdio.h>
#include "list.h"

list_p list_new() {
	list_p list = (list_p)malloc(sizeof(list_t));
	
	if (list == NULL) return NULL;
	
	list->head = NULL;
	list->cur = list->head;
	list->prev = NULL;
	
	return list;
}

node_p node_new(void *element) {
	node_p node = (node_p)malloc(sizeof(struct _node));
	
	if (node == NULL) return NULL;
	
	node->element = element;
	node->next = NULL;
	
	return node;
}

void node_set(node_p node, void *element) {
	node->element = element;
}

void *node_get(node_p node) {
	return node->element;
}

void node_reference_error() {
	fprintf(stderr, "ERROR: Trying to access unreferenced node.\n");
}

int list_is_empty(list_p list) {
	return list->head == NULL;
}

int list_cur_is_empty(list_p list) {
	return list->cur == NULL;
}

int list_at_first(list_p list) {
	return list->head == list->cur;
}

int list_at_end(list_p list) {
	int r;
	
	if (list_is_empty(list))
		r = 1;
	else if (list_cur_is_empty(list))
		r = 0;
	else
		r = (list->cur->next == NULL);
		
	return r;
}

int list_size(list_p list) {
	int count = 1;

	list_to_first(list);

	if (list_is_empty(list))
		return 0;

	while (!list_at_end(list)) {
		count++;
		list_advance(list);
	}

	return count;
}

void list_to_first(list_p list) {
	list->cur = list->head;
	list->prev = NULL;
}

void *list_advance(list_p list) {
	if (list_cur_is_empty(list)) {
		return NULL;
		node_reference_error();
	} else { 
		list->prev = list->cur;
		list->cur = list->cur->next;
		return list_get(list);
	}
}

void *list_get(list_p list) {
	if (list_cur_is_empty(list)) {
		node_reference_error();
		return NULL;
	}
	
	return list->cur->element;
}

list_p list_add(list_p list, void *element) {
	node_p node = node_new(element);
	
	if (list_is_empty(list)) {
		list->head = node;
		list->cur = node;
		list->prev = NULL;
	} else if (list_cur_is_empty(list)) {
		node_reference_error();
		return NULL;
	} else if (list_at_first(list)) {
		node->next = list->cur;
		list->head = node;
		list->cur = node;
	} else {
		node->next = list->cur;
		list->prev->next = node;
		list->cur = node;
	}
	return list;
}

node_p list_remove(list_p list) {
	node_p tmp;
	
	if (list_cur_is_empty(list)) {
		node_reference_error();
		return NULL;
	}
	
	if (list_at_first(list)) {
		tmp = list->cur;
		list->cur = list->cur->next;
		list->head = list->cur;
		return tmp;
	}

	tmp = list->cur;
	list->cur = list->prev;
	list->cur->next = tmp->next;

	if (!list_at_end(list))
		list_advance(list);

	return tmp;
}

void *node_free(node_p node) {
	void *tmp = node_get(node);
	free(node);
	return tmp;
}


void list_free(list_p list) {
	list_to_first(list);
	while (!list_is_empty(list))
		node_free(list_remove(list));
	free(list);
}
